package lectureInstructionUtilisateur;

import java.util.List;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import ecritureFichier.EcritureFichierResultat;
import ecritureFichier.EcritureFichierResultatHTML;

import plateau.FabriquePlateau;
import plateau.FabriquePlateauCirculaire;
import plateau.FabriquePlateauFerme;
import plateau.FabriquePlateauInfini;
import plateau.Plateau;

import analyseEvolutionPlateau.AnalyseEvolution;
import analyseEvolutionPlateau.AnalyseEvolutionTypeVaisseau;
import analyseEvolutionPlateau.ResultatAnalyse;
import lectureFichier.LectureDossier;
import lectureFichier.LectureFichier;
import lectureFichier.LectureFichierException;
import lectureFichier.LectureFichierLIF;

/**
 * Class ayant pour role la lectures des chaines de caractères envoyées au
 * programme et l'utilisation des differentes class du programme en fonctions de
 * ces instructions.
 */
public class LectureInstruction {

	/**
	 * Class implementant LectureFichier qui va être utilisé par les
	 * fabriquePlateaux (par defaut LectureFichierLif)
	 * 
	 * @see LectureFichier
	 * @see LectureFichierLif
	 */
	private static final LectureFichier lectureFichier = new LectureFichierLIF();

	/**
	 * L'analyse qui va être effectuée si l'utilisateur demande à faire une
	 * analyse de l'evolution asymptotique sur la/les configurations du jeu de
	 * la vie d'un fichier ou d'un dossier (par defaut
	 * AnalyseEvolutionTypeVaisseau)
	 * 
	 * @see AnalyseEvolution
	 * @see AnalyseEvolutionTypeVaisseau
	 */
	private static final AnalyseEvolution analyseEvolution = new AnalyseEvolutionTypeVaisseau();

	/**
	 * Class implementant EcritureFichier qui va être utilisé lors de l'écriture
	 * du fichier. (par defaut EcritureFichierResultatHtml)
	 * 
	 * @see EcritureFichier
	 * @see EcritureFichierResultatHtml
	 */
	private static final EcritureFichierResultat ecritureFichierResultat = new EcritureFichierResultatHTML();

	/**
	 * fabrique de plateau qui va être utilisé si l'utilisateur veux faire une
	 * simulation du jeu de la vie ou une analyse asymptotique. (par défaut
	 * FabriquePlateauInfini)
	 * 
	 * @see FabriquePlateau
	 * @see FabriquePlateauInfini
	 */
	public static FabriquePlateau fabriquePlateau = new FabriquePlateauInfini(
			lectureFichier);

	/**
	 * Class interne servant à ResultatInstructionner soit un message d'erreur,
	 * soit la réponce d'un des sous programme. (a été créé pour pouvoir
	 * verifier avec JUnit que la fonction lectureArguments fonctionnait bien)
	 */
	public static class ResultatInstruction {

		private String message;
		private int codeDerreur;

		// 0=execution sans erreur
		// 1=arguments invalides
		// 2=erreur lecture fichier

		public ResultatInstruction(String message, int codeDerreur) {
			assert (codeDerreur == 0 | codeDerreur == 1 | codeDerreur == 2);
			this.message = message;
			this.codeDerreur = codeDerreur;
		}

		public int getCodeDerreur() {
			return codeDerreur;
		}

		public String toString() {
			return message;
		}
	}

	public static void main(String[] args) {

		ResultatInstruction ResultatInstruction = lectureArguments(args);
		switch (ResultatInstruction.getCodeDerreur()) {
		case 0:
			System.out.println(ResultatInstruction.toString());
			break;
		case 1:
			System.err.println(ResultatInstruction.toString());
			System.err.println("utilisez -h pour afficher l'aide");
			break;
		case 2:
			System.err.println(ResultatInstruction.toString());
			break;
		default:
			break;
		}
	}

	/**
	 * Lit des chaines de caractères et si ces chaines sont invalide renvoit un
	 * message d'erreur, sinon execute les instructions associés à ces chaines
	 * de caractères.
	 * 
	 * @param args
	 *            les chaine de caractere que l'on veux analyser
	 * @return une variable de type ResultatInstruction contenant soit un
	 *         message d'erreur soit le resultat des fonctions appelé pour
	 *         remplir les instructions.
	 */
	public static ResultatInstruction lectureArguments(String[] args) {

		if (args != null && args.length != 0) {
			if (args[0].equals("-name")) {
				if (args.length != 1) {
					return new ResultatInstruction("entrez uniquement -name", 1);
				} else {
					return afficherNoms();
				}
			} else if (args[0].equals("-h")) {
				if (args.length != 1) {
					return new ResultatInstruction("entrez uniquement -h", 1);
				} else {
					return afficherOptions();
				}
			} else if (args[0].equals("-s")) {
				if (args.length != 3 && args.length != 6) {
					return new ResultatInstruction(
							" entrez: -s TempsMaximumDexecution nomDuFichier [ (-c|-f) tailleX tailleY ]",
							1);
				} else {
					if (args.length == 6) {
						ResultatInstruction ResultatInstruction = changerFabriquePlateau(args);
						if (ResultatInstruction.codeDerreur == 1) {
							return ResultatInstruction;
						}
					}

					try {
						int d = Integer.parseInt(args[1]);
						if (d <= 0) {
							return new ResultatInstruction(
									"Le deuxieme parametre doit etre un entier strictement positif",
									1);
						}
						String nom = args[2];
						simuler(d, nom);
						return new ResultatInstruction("fin de la simulation",
								0);
					} catch (NumberFormatException e) {
						return new ResultatInstruction(
								"Le deuxieme parametre doit etre un entier", 1);
					} catch (LectureFichierException e) {
						return new ResultatInstruction(e.getMessage(), 2);
					}
				}

			} else if (args[0].equals("-c")) {
				if (args.length != 3 && args.length != 6) {
					return new ResultatInstruction(
							" entrez -c TempsMaximumDexecution nomDuFichier [ (-c|-f) tailleX tailleY ] ",
							1);
				} else {

					if (args.length == 6) {
						ResultatInstruction ResultatInstruction = changerFabriquePlateau(args);
						if (ResultatInstruction.codeDerreur == 1) {
							return ResultatInstruction;
						}
					}
					try {
						int tempsMax = Integer.parseInt(args[1]);
						if (tempsMax < 0) {
							return new ResultatInstruction(
									"Le deuxieme parametre doit etre un entier positif",
									1);
						}
						return new ResultatInstruction(analyserEvolution(
								tempsMax, args[2]), 0);
					} catch (NumberFormatException e) {
						return new ResultatInstruction(
								"Le deuxieme parametre doit etre un entier", 1);
					} catch (LectureFichierException e) {
						return new ResultatInstruction(e.getMessage(), 2);
					}
				}
			} else if (args[0].equals("-w")) {
				if (args.length != 3 && args.length != 6) {
					return new ResultatInstruction(
							" entrez -w TempsMaximumDexecution nomDuDossier [ (-c|-f) tailleX tailleY ]",
							1);
				} else {

					if (args.length == 6) {
						ResultatInstruction ResultatInstruction = changerFabriquePlateau(args);
						if (ResultatInstruction.codeDerreur == 1) {
							return ResultatInstruction;
						}
					}

					try {
						int tempsMax = Integer.parseInt(args[1]);
						if (tempsMax < 0) {
							return new ResultatInstruction(
									"Le deuxieme parametre doit etre un entier positif",
									1);
						}
						analyserEvolutionDossier(tempsMax, args[2]);
						return new ResultatInstruction(
								"fin de l'ecriture du fichier", 0);
					} catch (NumberFormatException e) {

						return new ResultatInstruction(
								"Le deuxieme parametre doit etre un entier", 1);
					} catch (LectureFichierException e) {
						return new ResultatInstruction(e.getMessage(), 2);
					} catch (IOException e) {
						return new ResultatInstruction(e.getMessage(), 2);
					}
				}
			} else {
				return new ResultatInstruction(args[0] + ": argument invalide",
						1);
			}
		}
		return new ResultatInstruction("Veuillez entrer un argument.", 1);

	}

	/**
	 * affiche les noms des createurs du programme
	 */
	public static ResultatInstruction afficherNoms() {
		System.out.println("Bridoux Florian");
		System.out.println("Cochat Thomas");
		System.out.println("Daviau Amelie");
		System.out.println("Keith Bruno");
		return new ResultatInstruction("", 0);
	}

	/**
	 * affiche les differentes options du programme
	 * 
	 * @return un ResultatInstruction sans erreur et sans message si tout s'est
	 *         bien passé, un message d'erreur de type lectureFichier sinon
	 */
	public static ResultatInstruction afficherOptions() {

		InputStream ips;
		try {
			ips = LectureInstruction.class.getResourceAsStream("arguments");			
			InputStreamReader ipsr = new InputStreamReader(ips);
			BufferedReader br = new BufferedReader(ipsr);
			String ligne = br.readLine();

			while (ligne != null) {
				System.out.println(ligne);
				ligne = br.readLine();
			}
		} catch (FileNotFoundException e) {
			return new ResultatInstruction(
					"le fichier \"argument\" n'est pas present à la racine du dossier",
					2);
		} catch (IOException e) {
			return new ResultatInstruction(
					"erreur inatendue lors de la lecture du fichier \"argument\"",
					2);
		}
		return new ResultatInstruction("", 0);
	}

	/**
	 * simule l'evolution et affiche les differentes etapes d'une configuration
	 * du jeu de la vie
	 * 
	 * @param d
	 *            nombre maximal d'etape de simulation
	 * @param nomFichier
	 *            nom du fichier a simuler
	 */
	public static void simuler(int d, String nomFichier)
			throws LectureFichierException {

		Plateau plateau = fabriquePlateau.fabriquerPlateau(nomFichier);

		for (int i = 1; i <= d; i++) {
			System.out.println("numero de generation:" + i);
			if (!plateau.afficher()) {
				return;
			}
			plateau.etapeSuivante();
		}

	}

	/**
	 * analyse l'evolution asymptotique d'un plateau contenu dans un fichier LIF
	 * 
	 * @param d
	 *            nombre maximal d'etape consacre a chaque fichier LIF
	 * @param nomDossier
	 *            nom du dossier analysé
	 */
	public static String analyserEvolution(int tempsMax, String nomFichier)
			throws LectureFichierException {

		Plateau plateau = fabriquePlateau.fabriquerPlateau(nomFichier);
		return analyseEvolution.analyse(plateau, tempsMax).toString();
	}

	/**
	 * analyse l'evolution asymptotique de tout les fichier LIF contenu dans un
	 * dossier et ecrit les resultats dans un fichier html
	 * 
	 * @param d
	 *            nombre maximal d'etape consacre a chaque fichier LIF
	 * @param nomDossier
	 *            nom du dossier analysé
	 */
	public static void analyserEvolutionDossier(int d, String nomDossier)
			throws LectureFichierException, IOException {
		List<ResultatAnalyse> resultats = LectureDossier.analyserDossier(
				nomDossier, d, fabriquePlateau, analyseEvolution);
		ecritureFichierResultat.EcrireFichierHTML(resultats);

	}

	/**
	 * Change la fabriquePlateau, autrement dit le type de plateau qui sera
	 * utilisé pour faire executer les differentes instructions (simulation,
	 * analyse du comportement asymptotique etc...)
	 * 
	 * @param args
	 *            les arguments envoyé au programme
	 * @return un message d'erreur si les instructions pour choisir un autre
	 *         plateau ne sont pas correcte, un ResultatInstruction sans erreur
	 *         sinon.
	 */
	public static ResultatInstruction changerFabriquePlateau(String[] args) {
		assert (args.length == 6);
		try {
			int x = Integer.parseInt(args[4]);
			int y = Integer.parseInt(args[5]);
			if (x <= 0 | y <= 0) {
				return new ResultatInstruction(
						"le 5eme et 6eme arguments doivent être des entiers positifs",
						1);
			}

			if (args[3].equals("-c")) {
				fabriquePlateau = new FabriquePlateauCirculaire(lectureFichier,
						x, y);
			} else if (args[3].equals("-f")) {
				fabriquePlateau = new FabriquePlateauFerme(lectureFichier, x, y);
			} else {
				return new ResultatInstruction(
						"le 4eme argument (qui est facultatif) ne peut être que -c ou -f",
						1);
			}
		} catch (NumberFormatException e) {
			return new ResultatInstruction(
					"le 5eme et 6eme arguments doivent être des entiers strictements positifs",
					1);
		}

		return new ResultatInstruction("", 0);
	}
}
